package com.example.weblogaspect.aspect;

import com.example.weblogaspect.constant.OperationLogHeaderConstant;
import com.example.weblogaspect.model.OperationLogPgDto;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 *
 * aop 日志监听记录
 * 方式一
 */
@Slf4j
@Aspect
@Component
public class WebLogAspect {
    /**
     * 定义了一个切入点
     */
    private static final String POINT_CUT = "execution(public * com.*.*.controller..*.*(..))";
    ThreadLocal<Long> startTime = new ThreadLocal<>();
    private final ApplicationContext applicationContext;

    public WebLogAspect(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    //定义了一个切入点
    @Pointcut(POINT_CUT)
    public void webLogPg() {
    }

    /**
     * 执行前
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLogPg()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        //开始一个时间戳
        startTime.set(System.currentTimeMillis());
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            //把时间记录保存到当前会话中，其他地方调用
            attributes.setAttribute(OperationLogHeaderConstant.START_TIME, startTime.get(), 1);
            if (attributes.getRequest() != null) {
                HttpServletRequest request = attributes.getRequest();
                //记录各种参数日志
                String info = String.format("Method:{%s} Host:{%s} Path:{%s} Query:{%s}",
                        request.getMethod(),
                        request.getScheme(),
                        request.getRequestURL().toString(),
                        request.getParameterMap());
                log.info("gateway filter=>{}", info);
                //log.info("gateway filter.Query=>{}",);
                // 记录下请求内容
                log.info("URL : {}", request.getRequestURL().toString());
                log.info("HTTP_METHOD : {}", request.getMethod());
                log.info("IP : {}", request.getRemoteAddr());
                log.info("CLASS_METHOD : {}", joinPoint.getSignature().getDeclaringTypeName() + ":" + joinPoint.getSignature().getName());
                log.info("ARGS : {}", joinPoint.getArgs());
            }
        }
    }

    /**
     * 执行后，结束后
     * @param ret
     * @throws Throwable
     */
    @AfterReturning(returning = "ret", pointcut = "webLogPg()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求，返回内容
        log.debug("RESPONSE : {}", ret);
        log.info("SPEND TIME : {},", (System.currentTimeMillis() - startTime.get()));
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (null != attributes) {
            //查看其他地方是否有自定义日志
            OperationLogPgDto logVo = (OperationLogPgDto) attributes.getAttribute(OperationLogHeaderConstant.DO_AFTER_RETURNING_LOG, 1);
            if (null != logVo) {
                //如果有，那么使用自定义日志
                log.info("publishEvent : {}", logVo);
                //消息总线监听 处理
                applicationContext.publishEvent(logVo);
            }else{
                //如果没有，那么记录日志
                logVo = new OperationLogPgDto();
                logVo.setOperator("doAfterReturning");
                log.info("publishEvent : {}", logVo);
                //消息总线监听  处理
                applicationContext.publishEvent(logVo);
            }
        }
    }

    /**
     * 执行错误后
     * @param e
     */
    @AfterThrowing(value = POINT_CUT, throwing = "e")
    public void afterThrowing(Throwable e) {
        log.error("afterThrowing: " + e.getMessage(), e);
    }

    /**
     * RequestContextListener监听器
     *
     * @return
     */
    @Bean
    public RequestContextListener requestContextListener() {
        return new RequestContextListener();
    }
}